//package com.ss.project.xia17user.config.security.auth2;
//
//import com.ss.project.xia17user.config.security.SecurityUtils;
//import com.ss.project.xia17user.common.utils.ResponseUtil;
//import com.ss.project.xia17user.service.ClientService;
//import lombok.RequiredArgsConstructor;
//import org.springframework.http.HttpMethod;
//import org.springframework.security.authentication.AnonymousAuthenticationToken;
//import org.springframework.security.core.Authentication;
//import org.springframework.security.core.context.SecurityContextHolder;
//import org.springframework.security.crypto.keygen.Base64StringKeyGenerator;
//import org.springframework.security.crypto.keygen.StringKeyGenerator;
//import org.springframework.security.oauth2.core.AuthorizationGrantType;
//import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
//import org.springframework.security.oauth2.core.OAuth2Error;
//import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
//import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
//import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;
//import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
//import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
//import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
//import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
//import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
//import org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter;
//import org.springframework.security.web.DefaultRedirectStrategy;
//import org.springframework.security.web.RedirectStrategy;
//import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
//import org.springframework.security.web.util.matcher.RequestMatcher;
//import org.springframework.util.LinkedMultiValueMap;
//import org.springframework.util.MultiValueMap;
//import org.springframework.util.StringUtils;
//import org.springframework.web.filter.OncePerRequestFilter;
//import org.springframework.web.util.UriComponentsBuilder;
//import javax.servlet.FilterChain;
//import javax.servlet.ServletException;
//import javax.servlet.http.HttpServletRequest;
//import javax.servlet.http.HttpServletResponse;
//import javax.validation.constraints.NotNull;
//import java.io.IOException;
//import java.util.*;
//
///**
// * Oauth2 client 认证Filter
// * 代码大部分来源 OAuth2AuthorizationEndpointFilter , 这里增加了判断用户是否可以访问Client的机制
// * @author xia17
// * @date 2020/10/15 15:38
// * @see OAuth2AuthorizationEndpointFilter
// */
//@RequiredArgsConstructor
//public class CustomAuth2AuthorizationEndpointFilter extends OncePerRequestFilter {
//
//    private final RegisteredClientRepository registeredClientRepository;
//    private final OAuth2AuthorizationService authorizationService;
//    private final ClientService clientService;
//    private final RequestMatcher authorizationEndpointMatcher = new AntPathRequestMatcher(
//            OAuth2AuthorizationEndpointFilter.DEFAULT_AUTHORIZATION_ENDPOINT_URI, HttpMethod.GET.name());
//    private final StringKeyGenerator codeGenerator = new Base64StringKeyGenerator(Base64.getUrlEncoder());
//    private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
//
//
//    @Override
//    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
//            throws ServletException, IOException {
//
//        // 是否匹配 /oauth2/authorize 请求
//        if (!this.authorizationEndpointMatcher.matches(request)) {
//            // 不匹配则执行下一个过滤器
//            filterChain.doFilter(request, response);
//            return;
//        }
//
//        // ---------------
//        // Validate the request to ensure that all required parameters are present and valid
//        // 验证请求以确保所有必需的参数都存在且有效
//        // ---------------
//
//        MultiValueMap<String, String> parameters = getParameters(request);
//        // 获取state参数
//        String stateParameter = parameters.getFirst(OAuth2ParameterNames.STATE);
//
//        //  获取 client_id参数 client_id (REQUIRED)
//        String clientId = parameters.getFirst(OAuth2ParameterNames.CLIENT_ID);
//        if (!StringUtils.hasText(clientId) ||
//                parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) {
//            // 如果没有client_id参数 报错
//            OAuth2Error error = createError(OAuth2ErrorCodes.INVALID_REQUEST,"缺少" + OAuth2ParameterNames.CLIENT_ID + "参数");
//            // when redirectUri is null then don't redirect
//            throwError(request,response,error);
//            return;
//        }
//        // 通过 registeredClientRepository 获取Client信息
//        RegisteredClient registeredClient = this.registeredClientRepository.findByClientId(clientId);
//        if (registeredClient == null) {
//            // 如果没有获取到Client信息 ，则报错
//            OAuth2Error error = createError(OAuth2ErrorCodes.INVALID_REQUEST, "client_id[" + clientId + "]不存在");
//            // when redirectUri is null then don't redirect
//            throwError(request,response,error);
//            return;
//        } else if (!registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.AUTHORIZATION_CODE)) {
//            // 如果client信息中的 AuthorizationGrantTypes 没有包含 AuthorizationGrantType.AUTHORIZATION_CODE 方式 ， 则报错
//            // 因为现在还是0.0.1版本 ， 暂时只支持 AUTHORIZATION_CODE 认证模式
//            OAuth2Error error = createError(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT, "暂时只支持 AUTHORIZATION_CODE 授权模式");
//            // when redirectUri is null then don't redirect
//            throwError(request,response,error);
//            return;
//        }
//
//        // redirect_uri (OPTIONAL) 获取请求参数中的的跳转地址
//        String redirectUriParameter = parameters.getFirst(OAuth2ParameterNames.REDIRECT_URI);
//        if (StringUtils.hasText(redirectUriParameter)) {
//            // 如果参数中含有跳转地址则执行这里
//            if (!registeredClient.getRedirectUris().contains(redirectUriParameter) ||
//                    parameters.get(OAuth2ParameterNames.REDIRECT_URI).size() != 1) {
//                // 判断 Client信息中有没有这个参数中的跳转地址，如果没有报错
//                OAuth2Error error = createError(OAuth2ErrorCodes.INVALID_REQUEST, "授权回调地址不存在");
//                // when redirectUri is null then don't redirect
//                throwError(request,response,error);
//                return;
//            }
//        } else if (registeredClient.getRedirectUris().size() != 1) {
//            // 如果参数中没有uri ， 且Client信息中的跳转地址不是唯一一个，那么报错
//            OAuth2Error error = createError(OAuth2ErrorCodes.INVALID_REQUEST, "请指定一个授权回调地址");
//            // when redirectUri is null then don't redirect
//            throwError(request,response,error);
//            return;
//        }
//        // 获取最终的跳转地址 ， 如果参数中有跳转地址 则使用参数中的地址，不然使用Client信息中的跳转地址
//        String redirectUri = StringUtils.hasText(redirectUriParameter) ?
//                redirectUriParameter : registeredClient.getRedirectUris().iterator().next();
//
//        // response_type (REQUIRED) 获取参数中的认证方式
//        String responseType = parameters.getFirst(OAuth2ParameterNames.RESPONSE_TYPE);
//        if (!StringUtils.hasText(responseType) ||
//                parameters.get(OAuth2ParameterNames.RESPONSE_TYPE).size() != 1) {
//            // 如果参数中没有认证方式，那么报错
//            OAuth2Error error = createError(OAuth2ErrorCodes.INVALID_REQUEST, "缺少" + OAuth2ParameterNames.RESPONSE_TYPE + "参数");
//            sendErrorResponse(request, response, error, stateParameter, redirectUri);
//            return;
//        } else if (!responseType.equals(OAuth2AuthorizationResponseType.CODE.getValue())) {
//            // 如果认证方式不是 code 那么直接报错 ， 0.0.1只支持  AUTHORIZATION_CODE 认证模式
//            OAuth2Error error = createError(OAuth2ErrorCodes.UNSUPPORTED_RESPONSE_TYPE, "只支持  AUTHORIZATION_CODE 认证模式");
//            sendErrorResponse(request, response, error, stateParameter, redirectUri);
//            return;
//        }
//
//        // ---------------
//        // The request is valid - ensure the resource owner is authenticated
//        // 请求有效-请确保资源所有者经过身份验证
//        // 执行到这里说明认证式有效的，后面需要的是 身份认证
//        // ---------------
//        // 获取认证信息
//        Authentication principal = SecurityContextHolder.getContext().getAuthentication();
//        if (!isPrincipalAuthenticated(principal)) {
//            // Pass through the chain with the expectation that the authentication process
//            // will commence via AuthenticationEntryPoint
//            // 通过链，期望身份验证过程
//            // 将通过AuthenticationEntryPoint开始
//            // 执行下一个过滤器 ， 这里说一下，security默认的登录成功后会跳转到上一个页面，也就是说，登录成功后会重新执行这个请求。从而实现登录成功后继续认证过程
//            filterChain.doFilter(request, response);
//            return;
//        }
//
//        // ------------- xia17 写的以下代码 ---------------------
//        // 判断用户是否可以访问这个客户端 ， 如果不可以访问报错
//        if (!clientService.checkUserClientPermission(SecurityUtils.userId(),clientId)){
//            logger.error("当前登录的用户[" + SecurityUtils.userId() +  "]不能访问ClientId[" + clientId + "]");
//            throwError(request,response,new OAuth2Error(OAuth2ErrorCodes.ACCESS_DENIED,"你没有权限访问该项目",""));
//            return;
//        }
//        // --------------xia17 写的代码结束 ---------------------
//
//
//        // 生成code ，用于获取 accessToken
//        String code = this.codeGenerator.generateKey();
//        // 将 request请求转换成 OAuth2AuthorizationRequest
//        OAuth2AuthorizationRequest authorizationRequest = convertAuthorizationRequest(request);
//        // 生成一个 OAuth2Authorization ，存储了认证信息
//        OAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(registeredClient)
//                .principalName(principal.getName())
//                .attribute(OAuth2AuthorizationAttributeNames.CODE, code)
//                .attribute(OAuth2AuthorizationAttributeNames.AUTHORIZATION_REQUEST, authorizationRequest)
//                .build();
//        // 调用authorizationService 保存认证信息
//        this.authorizationService.save(authorization);
//
////		TODO security checks for code parameter
////		The authorization code MUST expire shortly after it is issued to mitigate the risk of leaks.
////		A maximum authorization code lifetime of 10 minutes is RECOMMENDED.
////		The client MUST NOT use the authorization code more than once.
////		If an authorization code is used more than once, the authorization server MUST deny the request
////		and SHOULD revoke (when possible) all tokens previously issued based on that authorization code.
////		The authorization code is bound to the client identifier and redirection URI.
////      代码参数的TODO安全检查
////      授权码必须在发布后不久过期，以降低泄漏风险。
////      建议授权码的最长生存期为10分钟。
////      客户端不能多次使用授权码。
////      如果一个授权码被多次使用，授权服务器必须拒绝该请求
////      并且应该撤销（如果可能的话）之前基于该授权码发行的所有令牌。
////      授权代码绑定到客户端标识符和重定向URI。
//        // 跳转到回调地址
//        sendAuthorizationResponse(request, response, authorizationRequest, code, redirectUri);
//    }
//
//
//
//    private void sendAuthorizationResponse(HttpServletRequest request, HttpServletResponse response,
//                                           OAuth2AuthorizationRequest authorizationRequest, String code, String redirectUri) throws IOException {
//
//        UriComponentsBuilder uriBuilder = UriComponentsBuilder
//                .fromUriString(redirectUri)
//                .queryParam(OAuth2ParameterNames.CODE, code);
//        if (StringUtils.hasText(authorizationRequest.getState())) {
//            uriBuilder.queryParam(OAuth2ParameterNames.STATE, authorizationRequest.getState());
//        }
//        this.redirectStrategy.sendRedirect(request, response, uriBuilder.toUriString());
//    }
//
//    private void sendErrorResponse(HttpServletRequest request, HttpServletResponse response,
//                                   OAuth2Error error, String state,@NotNull String redirectUri) throws IOException {
//
//        UriComponentsBuilder uriBuilder = UriComponentsBuilder
//                .fromUriString(redirectUri)
//                .queryParam(OAuth2ParameterNames.ERROR, error.getErrorCode());
//        if (StringUtils.hasText(error.getDescription())) {
//            uriBuilder.queryParam(OAuth2ParameterNames.ERROR_DESCRIPTION, error.getDescription());
//        }
//        if (StringUtils.hasText(error.getUri())) {
//            uriBuilder.queryParam(OAuth2ParameterNames.ERROR_URI, error.getUri());
//        }
//        if (StringUtils.hasText(state)) {
//            uriBuilder.queryParam(OAuth2ParameterNames.STATE, state);
//        }
//        this.redirectStrategy.sendRedirect(request, response, uriBuilder.toUriString());
//    }
//
//    /**
//     * 抛出异常
//     * @param error /
//     */
//    private void throwError(HttpServletRequest request, HttpServletResponse response, OAuth2Error error){
//        ResponseUtil.sendError(request,response,new OAuth2AuthenticationException(error));
//    }
//
//    private static MultiValueMap<String, String> getParameters(HttpServletRequest request) {
//        Map<String, String[]> parameterMap = request.getParameterMap();
//        MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>(parameterMap.size());
//        parameterMap.forEach((key, values) -> {
//            if (values.length > 0) {
//                for (String value : values) {
//                    parameters.add(key, value);
//                }
//            }
//        });
//        return parameters;
//    }
//
//    private static OAuth2Error createError(String errorCode, String msg ) {
//        return new OAuth2Error(errorCode, "请求参数异常: " + msg,
//                "https://tools.ietf.org/html/rfc6749#section-4.1.2.1");
//    }
//
//    private static boolean isPrincipalAuthenticated(Authentication principal) {
//        return principal != null &&
//                !AnonymousAuthenticationToken.class.isAssignableFrom(principal.getClass()) &&
//                principal.isAuthenticated();
//    }
//
//    private static OAuth2AuthorizationRequest convertAuthorizationRequest(HttpServletRequest request) {
//        MultiValueMap<String, String> parameters = getParameters(request);
//
//        Set<String> scopes = Collections.emptySet();
//        if (parameters.containsKey(OAuth2ParameterNames.SCOPE)) {
//            String scope = parameters.getFirst(OAuth2ParameterNames.SCOPE);
//            scopes = new HashSet<>(Arrays.asList(StringUtils.delimitedListToStringArray(scope, " ")));
//        }
//
//        return OAuth2AuthorizationRequest.authorizationCode()
//                .authorizationUri(request.getRequestURL().toString())
//                .clientId(parameters.getFirst(OAuth2ParameterNames.CLIENT_ID))
//                .redirectUri(parameters.getFirst(OAuth2ParameterNames.REDIRECT_URI))
//                .scopes(scopes)
//                .state(parameters.getFirst(OAuth2ParameterNames.STATE))
//                .additionalParameters(additionalParameters ->
//                        parameters.entrySet().stream()
//                                .filter(e -> !e.getKey().equals(OAuth2ParameterNames.RESPONSE_TYPE) &&
//                                        !e.getKey().equals(OAuth2ParameterNames.CLIENT_ID) &&
//                                        !e.getKey().equals(OAuth2ParameterNames.REDIRECT_URI) &&
//                                        !e.getKey().equals(OAuth2ParameterNames.SCOPE) &&
//                                        !e.getKey().equals(OAuth2ParameterNames.STATE))
//                                .forEach(e -> additionalParameters.put(e.getKey(), e.getValue().get(0))))
//                .build();
//    }
//
//}
